package com.github.sbb.sherry.console.exception;

import com.alibaba.fastjson.JSON;
import com.github.sbb.sherry.console.interceptor.AppLogHolder;
import java.io.PrintWriter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

/**
 * @author 张瑀楠 zyndev@gmail.com
 */
@Slf4j
@Component
public class GlobalExceptionResolver implements HandlerExceptionResolver {

  // 此处可针对 handler 做特殊处理
  @Override
  @Nullable
  public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response,
      Object handler, Exception ex) {

    int status = HttpStatus.INTERNAL_SERVER_ERROR.value();
    ErrorMessage errorMessage;
    ErrorOutputLogLevelEnum outputLogLevelEnum = ErrorOutputLogLevelEnum.ERROR;
    String logPrefix = "";

    if (handler instanceof HandlerMethod) {
      ErrorLogPrefix errorLogPrefix = ((HandlerMethod) handler).getMethodAnnotation(ErrorLogPrefix.class);
      if (errorLogPrefix != null) {
        logPrefix = logPrefix.concat(errorLogPrefix.value());
      }
      logPrefix = logPrefix.concat(((HandlerMethod) handler).getShortLogMessage());
    }

    if (ex instanceof IllegalArgumentException) {
      errorMessage = new ErrorMessage(ErrorCode.BAD_REQUEST, ex.getMessage());
      status = HttpStatus.BAD_REQUEST.value();
    } else if (ex instanceof BaseException) {
      BaseException err = (BaseException) ex;
      status = err.getHttpStatus().value();
      errorMessage = err.getErrorMessage();
      outputLogLevelEnum = err.getLogLevel();
    } else {
      errorMessage = ErrorMessage.SERVER_ERROR;
    }

    switch (outputLogLevelEnum) {
      case ERROR:
        log.error("{} url: {} error failed, status:{}", logPrefix, request.getRequestURI(), status, ex);
        break;
      case WARN:
        log.warn("{} url: {} error failed, status:{}", logPrefix, request.getRequestURI(), status, ex);
        break;
      case NONE:
        // NONE
        break;
    }

    errorMessage.setLogCode(AppLogHolder.get().getLogNo());

    sendError(response, HttpStatus.valueOf(status).value(), errorMessage);
    return new ModelAndView();
  }

  /**
   * 发送错误信息
   *
   * @param rsp          HttpServletResponse
   * @param httpCode     httpCode
   * @param errorMessage message
   */
  private void sendError(HttpServletResponse rsp, int httpCode, ErrorMessage errorMessage) {
    PrintWriter pw = null;
    try {
      rsp.setCharacterEncoding("utf-8");
      rsp.setContentType("application/json;charset=UTF-8");
      rsp.setStatus(httpCode);
      pw = rsp.getWriter();
      String body = JSON.toJSONString(errorMessage);
      pw.println(body);
      pw.flush();
    } catch (Exception e) {
      log.error("GlobalExceptionResolver sendError ", e);
    } finally {
      if (pw != null) {
        pw.close();
      }
    }
  }

}
